package edu.pku.sei.mte.compositetrans.atl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.m2m.atl.core.ATLCoreException;
import org.eclipse.m2m.atl.core.IExtractor;
import org.eclipse.m2m.atl.core.IInjector;
import org.eclipse.m2m.atl.core.IModel;
import org.eclipse.m2m.atl.core.IReferenceModel;
import org.eclipse.m2m.atl.core.ModelFactory;
import org.eclipse.m2m.atl.core.emf.EMFExtractor;
import org.eclipse.m2m.atl.core.emf.EMFInjector;
import org.eclipse.m2m.atl.core.emf.EMFModelFactory;
import org.eclipse.m2m.atl.core.launch.ILauncher;
import org.eclipse.m2m.atl.core.service.CoreService;
import org.eclipse.m2m.atl.engine.emfvm.launch.EMFVMLauncher;
import org.w3c.dom.Element;

import edu.pku.sei.mte.compositetrans.TResult;
import edu.pku.sei.mte.compositetrans.UniTController;
import edu.pku.sei.mte.compositetrans.configuration.MetaModelSpec;
import edu.pku.sei.mte.compositetrans.configuration.ModelSpec;
import edu.pku.sei.mte.compositetrans.configuration.TransDescriptor;
import edu.pku.sei.mte.utilities.ModelFormat;

public class ATLController extends UniTController {

	public ATLController(Element root) {
		super(ModelFormat.EMF, root);
	}

	private final static String ATL_FORMAT_NAME = "EMF";
	private final static String FILE_PREFIX = "file:/";
	private ILauncher launcher;
	private IExtractor extractor;
	private IInjector injector;
	private ModelFactory factory;
	private Map<String, IReferenceModel> MMMap;
	private ModelSpec targetModelSpec = null;
	private BufferedInputStream trans_file;
	
	static {
		CoreService.registerLauncher(new EMFVMLauncher());
		CoreService.registerFactory("EMF", EMFModelFactory.class); 
		CoreService.registerExtractor("EMF", new EMFExtractor());
		CoreService.registerInjector("EMF", new EMFInjector());
	}
	
	@Override
	public boolean launch(){
		launcher.launch(ILauncher.RUN_MODE, new NullProgressMonitor(), Collections.<String, Object> emptyMap(), trans_file);
		result.isSucceed = true;
		return true;
	}

	@Override
	protected boolean init() {
		try {
			injector = CoreService.getInjector(ATL_FORMAT_NAME);
		
			extractor = CoreService.getExtractor(ATL_FORMAT_NAME);
			factory = CoreService.createModelFactory(ATL_FORMAT_NAME);
			
			// Metamodels
			MMMap = new HashMap<String, IReferenceModel>();
			for(MetaModelSpec fs : tconf.metaModels){
				IReferenceModel metamodel = factory.newReferenceModel();
				injector.inject(metamodel, FILE_PREFIX + fs.filePath);
				MMMap.put(fs.name, metamodel);
			}
			IReferenceModel refiningTraceMetamodel = factory.getBuiltInResource("RefiningTrace.ecore");
	
			// Getting launcher
			launcher = CoreService.getLauncher(ATL_FORMAT_NAME + "-specific VM");
			launcher.initialize(Collections.<String, Object> emptyMap());
	
			// Creating models
			IModel refiningTraceModel = factory.newModel(refiningTraceMetamodel);
			launcher.addOutModel(refiningTraceModel, "refiningTrace", "RefiningTrace");
			File tfile = new File(tconf.props.getProperty(TransDescriptor.TRANS_FILE_PATH));
			trans_file = new BufferedInputStream(new FileInputStream(tfile));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		return true;
	}

	@Override
	protected boolean loadModel(TResult result, ModelSpec ms) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	protected boolean loadModel(ModelSpec ms) {
		if(MMMap.containsKey(ms.typeName)){
			IModel model= null;
			try {
				model = factory.newModel(MMMap.get(ms.typeName));
				File f = new File(ms.filePath);
				if(!f.exists())
					f.createNewFile();
				injector.inject(model, FILE_PREFIX + ms.filePath);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			}
			if(tconf.props.getProperty(TransDescriptor.DIRECTION).equals(ms.name)){
				launcher.addInOutModel(model, ms.name, ms.typeName);
				targetModelSpec = ms;
				result.targetModel = ms.filePath;
				for(MetaModelSpec fs : tconf.metaModels)
					if(fs.name.equals(ms.typeName))
						result.targetModelType = fs.filePath;
			}
			else launcher.addInModel(model, ms.name, ms.typeName);
		}else return false;
		return true;
	}

	@Override
	protected boolean saveModel() {
		if(targetModelSpec != null){
			try {
				extractor.extract((IModel) targetModelSpec.model, FILE_PREFIX + result.targetModel);
			} catch (ATLCoreException e) {
				e.printStackTrace();
				return false;
			}
			return true;
		}
		return false;
	}
}
